package com.aliothservice.catfish.cart.data;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import com.aliothservice.catfish.cart.request.AddProductIntoCartRequest;
import com.aliothservice.catfish.cart.request.AddVehicleIntoCartRequest;
import com.aliothservice.catfish.cart.request.AddWarehouseIntoCartRequest;
import com.aliothservice.catfish.product.data.ProductService;
import com.aliothservice.catfish.product.data.VehicleService;
import com.aliothservice.catfish.product.data.WarehouseService;
import com.mongodb.WriteResult;

public class CartDataManager {
	
	public boolean addWarehouseIntoCart(String phoneNumber, AddWarehouseIntoCartRequest req, WarehouseService warehouseService) {
		// all warehouse with the same service id and dates will be combined
		Query query = new Query();
		Criteria idCriteria = Criteria.where("warehouseId").is(req.getWarehouseId());
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		Date startDate;
		Date untilDate;
		try {
			startDate = formatter.parse(req.getStartDate());
			untilDate = formatter.parse(req.getUntilDate());
		} catch (ParseException e) {
			logger.warn("addVehicleIntoCart - invalid date format, user: " + phoneNumber);
			return false;
		}
		Criteria startDateCriteria = Criteria.where("startDate").is(startDate);;
		Criteria untilDateCriteria = Criteria.where("untilDate").is(untilDate);;
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, startDateCriteria, untilDateCriteria, userCriteria);
		query.addCriteria(andCriteria);
		
		Update update = new Update();
		update.set("userId", phoneNumber);
		update.set("warehouseId", req.getWarehouseId());
		update.set("startDate", startDate);
		update.set("untilDate", untilDate);
		update.set("serviceObject", warehouseService.mapForSearchResult());
		update.inc("volume", req.getVolume());
		
		WriteResult result = mongoTemplate.upsert(query, update, Cart.class);
		if (1 != result.getN()) {
			return false;
		}
		
		return true;
	}
	
	public boolean addProductIntoCart(String phoneNumber, AddProductIntoCartRequest req, ProductService productService) {
		// all products with the same service id will be combined
		Query query = new Query();
		Criteria idCriteria = Criteria.where("productId").is(req.getProductId());
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria);
		query.addCriteria(andCriteria);
		
		Update update = new Update();
		update.set("userId", phoneNumber);
		update.set("productId", req.getProductId());
		update.set("serviceObject", productService.mapForSearchResult());
		update.inc("volume", req.getVolume());
		
		WriteResult result = mongoTemplate.upsert(query, update, Cart.class);
		if (1 != result.getN()) {
			return false;
		}
		
		return true;
	}
	
	public boolean addVehicleIntoCart(String phoneNumber, AddVehicleIntoCartRequest req, VehicleService vehicleService) {
		// all vehicles with the same service id will be overwritten
		Query query = new Query();
		Criteria idCriteria = Criteria.where("vehicleId").is(req.getVehicleId());
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria);
		query.addCriteria(andCriteria);
		
		Update update = new Update();
		update.set("userId", phoneNumber);
		update.set("vehicleId", req.getVehicleId());
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		Date loadingDate;
		try {
			loadingDate = formatter.parse(req.getLoadingDate());
		} catch (ParseException e) {
			logger.warn("addVehicleIntoCart - invalid date format, user: " + phoneNumber);
			return false;
		}
		update.set("loadingDate", loadingDate);
		update.set("temperatureSpec", req.getTemperatureSpec());
		update.set("serviceObject", vehicleService.mapForSearchResult());
		
		WriteResult result = mongoTemplate.upsert(query, update, Cart.class);
		if (1 != result.getN()) {
			return false;
		}
		
		return true;
	}
	
	public List<Map<String, Object>> myCart(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		query.addCriteria(userCriteria);
		
		List<Cart> list = mongoTemplate.find(query, Cart.class);
		if (logger.isDebugEnabled()) {
			logger.debug("myCart, request by " + phoneNumber + " and " + list.size() + " records returned");
		}
		for (Cart cart : list) {
			result.add(cart.toMap());
		}
		
		return result;
	}
	
	public boolean deleteServicesFromCart(String phoneNumber, List<String> idList) {
		Query query = new Query();
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria idListCriteria = Criteria.where("id").in(idList);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, idListCriteria);
		query.addCriteria(andCriteria);
		
		WriteResult result = mongoTemplate.remove(query, Cart.class);
		if (idList.size() != result.getN()) {
			if (logger.isDebugEnabled()) {
				logger.debug("deleteServicesFromCart - getN:" + Integer.valueOf(result.getN()));
			}
			return false;
		}
		
		return true;
	}
	
	public boolean deleteServicesFromCartById(String phoneNumber, List<String> idList) {
		Query query = new Query();
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria vehicleCriteria = Criteria.where("vehicleId").in(idList);
		Criteria warehouseCriteria = Criteria.where("warehouseId").in(idList);
		Criteria productCriteria = Criteria.where("productId").in(idList);
		Criteria serviceCriteria = new Criteria();
		serviceCriteria.orOperator(vehicleCriteria, warehouseCriteria, productCriteria);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, serviceCriteria);
		query.addCriteria(andCriteria);
		
		WriteResult result = mongoTemplate.remove(query, Cart.class);
		if (logger.isDebugEnabled()) {
			logger.debug("deleteServicesFromCartById - getN:" + Integer.valueOf(result.getN()));
		}
		if (result.getN() != idList.size()) {
			return false;
		} else {
			return true;
		}
	}
	
	public void myCartCounting(String phoneNumber, Map<String, String> map) {
		Query query = new Query();
		Criteria criteria = Criteria.where("userId").is(phoneNumber);
		query.addCriteria(criteria);
		
		long count = mongoTemplate.count(query, Cart.class);
		map.put("count", Long.valueOf(count).toString());
	}
	
	@Autowired
	private MongoTemplate mongoTemplate;

	private static final Logger logger = LoggerFactory.getLogger(CartDataManager.class);
}
